// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using ILLink.Shared;
using ILLink.Shared.DataFlow;
using Microsoft.CodeAnalysis.Diagnostics;

namespace ILLink.RoslynAnalyzer.DataFlow
{
    public struct FeatureContext : IEquatable<FeatureContext>, IDeepCopyValue<FeatureContext>
    {
        // The set of features known to be enabled in this context.
        // Unknown represents "all possible features".
        public ValueSet<string> EnabledFeatures;

        public static readonly FeatureContext All = new FeatureContext(ValueSet<string>.Unknown);

        public static readonly FeatureContext None = new FeatureContext(ValueSet<string>.Empty);

        public FeatureContext(ValueSet<string> enabled)
        {
            EnabledFeatures = enabled;
        }

        public bool IsEnabled(string feature)
        {
            return EnabledFeatures.IsUnknown() || EnabledFeatures.Contains(feature);
        }

        public bool Equals(FeatureContext other) => EnabledFeatures == other.EnabledFeatures;
        public override bool Equals(object? obj) => obj is FeatureContext other && Equals(other);
        public override int GetHashCode() => EnabledFeatures.GetHashCode();

        public static bool operator ==(FeatureContext left, FeatureContext right) => left.Equals(right);
        public static bool operator !=(FeatureContext left, FeatureContext right) => !left.Equals(right);

        public FeatureContext DeepCopy()
        {
            return new FeatureContext(EnabledFeatures.DeepCopy());
        }

        public FeatureContext Intersection(FeatureContext other)
        {
            return new FeatureContext(ValueSet<string>.Intersection(EnabledFeatures, other.EnabledFeatures));
        }

        public FeatureContext Union(FeatureContext other)
        {
            return new FeatureContext(ValueSet<string>.Union(EnabledFeatures, other.EnabledFeatures));
        }

        public override string ToString()
        {
            if (EnabledFeatures.IsUnknown())
                return "All";
            if (EnabledFeatures.IsEmpty())
                return "None";
            return string.Join(", ", EnabledFeatures.GetKnownValues());
        }
    }

    public readonly struct FeatureContextLattice : ILattice<FeatureContext>
    {
        public FeatureContextLattice() { }

        // The top value is the identity of meet (intersection), the set of all features.
        public FeatureContext Top { get; } = FeatureContext.All;

        // We are interested in features which are known to be enabled for all paths through the CFG,
        // so the meet operator is set intersection.
        public FeatureContext Meet(FeatureContext left, FeatureContext right) => left.Intersection(right);
    }
}
